package repository

import (
	"happy-sudoku/internal/codec"
	"happy-sudoku/internal/model"

	"github.com/go-redis/redis"
	"github.com/jinzhu/gorm"
	"github.com/pkg/errors"
)

// IAuthRepo is to be implemented by
// AuthRepo
type IAuthRepo interface {
	GetOneByCookie(cookie string) (*model.Auth, error)
	UpdateByCookie(cookie string, auth model.Auth) error
	DeleteByCookie(cookie ...string) error
	CreateByCookie(cookie string, auth model.Auth) error

	GetPlayerByToken(token string) (*model.Player, error)
}

const (
	keyAuth = "player:auth"
)

func hAuthFieldID(cookie string) string {
	return cookie
}

// AuthRepo implements IPlayerStatRepo
// Use redis as data layer
// Use Hash "player:auth"
// Fields: cookie1, cookie2, cookie3...
type AuthRepo struct {
	rdb *redis.Client
	db  *gorm.DB
}

// NewAuthRepo new Auth repo
func NewAuthRepo(rdb *redis.Client, db *gorm.DB) *AuthRepo {
	return &AuthRepo{
		rdb: rdb,
		db:  db,
	}
}

// GetOneByCookie returns one auth
func (r *AuthRepo) GetOneByCookie(cookie string) (*model.Auth, error) {
	data, err := r.rdb.HGet(keyAuth, (cookie)).Result()
	if err != nil {
		return nil, errors.Wrap(err, "auth get one")
	}
	// unmarshal
	auth := model.MakeAuth()
	err = auth.Unmarshal(data)
	if err != nil {
		return nil, errors.Wrap(err, "auth get one")
	}
	return &auth, nil
}

// CreateByCookie creates Auth in cookie
func (r *AuthRepo) CreateByCookie(cookie string, auth model.Auth) error {
	data, err := auth.Marshal()
	if err != nil {
		return errors.Wrap(err, "auth create")
	}
	err = r.rdb.HSet(keyAuth, hAuthFieldID(cookie), data).Err()
	return err
}

// UpdateByCookie updates auth
func (r *AuthRepo) UpdateByCookie(cookie string, auth model.Auth) error {
	data, err := auth.Marshal()
	if err != nil {
		return errors.Wrap(err, "auth update")
	}
	err = r.rdb.HSet(keyAuth, hAuthFieldID(cookie), data).Err()
	if err != nil {
		return errors.Wrapf(err, "auth update: cookie %s", cookie)
	}
	return nil
}

// DeleteByCookie deletes auth
func (r *AuthRepo) DeleteByCookie(cookies ...string) error {
	err := r.rdb.HDel(keyAuth, cookies...).Err()
	if err != nil {
		return errors.Wrap(err, "auth delete")
	}
	return err
}

// GetPlayerByToken gets player by token
func (r *AuthRepo) GetPlayerByToken(token string) (*model.Player, error) {
	object := model.NewPlayer()
	res := r.db.Where("token = ?", token).Take(&object)
	if gorm.IsRecordNotFoundError(res.Error) {
		return nil, codec.ErrNoToken
	}
	return object, res.Error
}
